﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NumberRecognition.Interfaces;
using NumberRecognition.Models;
using System.IO;
using System.Drawing;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

namespace NumberRecognition.Presenters
{
    public class MainFormPresenter
    {
        private static int PictureSize = 80 * 80;
        private const string FileName = "NeuralNetState.txt";
        private IMainFormPresenter mainForm;
        private NeuralNet neuralNet;
        private int numberOfEpochs;

        private string PathToLearningSet { get; set; }

        public MainFormPresenter(IMainFormPresenter f, float learningSpeed, int epochs)
        {
            mainForm = f;
            numberOfEpochs = epochs;
            //numberOfEpochs = 1;
            //learningSpeed = 10.0F;

            if (!File.Exists(FileName))
                neuralNet = new NeuralNet(learningSpeed, PictureSize);
            else
            {
                Stream stream = null;
                try
                {
                    IFormatter formatter = new BinaryFormatter();
                    stream = File.Open(FileName, FileMode.Open);
                    neuralNet = (NeuralNet)formatter.Deserialize(stream);
                }
                catch
                {
                    neuralNet = new NeuralNet(learningSpeed, PictureSize);
                }
                finally
                {
                    if (stream != null)
                        stream.Close();
                }
            }
        }

        public void SetLearningElementEnabled(bool enabled)
        {
            mainForm.LearningSpeedEnabled = mainForm.NumberOfEpochsEnabled = mainForm.StartLearningButton = enabled;
        }

        public void StartLearning()
        {
            PathToLearningSet = "../../numbers/"; /////////// ПОТОМ УБРАТЬ!!!!!!!!!!!!!

            List<Tuple<string, int>> images = new List<Tuple<string, int>>();

            foreach (var imgName in Directory.GetFiles(PathToLearningSet, "*.jpeg"))
            {
                images.Add(new Tuple<string, int>(imgName, Convert.ToInt32(imgName.Substring(imgName.LastIndexOf('_') + 1, 1))));
            }

            Learn(images);
        }

        private void Learn(List<Tuple<string, int>> images)
        {
            Bitmap image;
            float[] Eav = new float[numberOfEpochs];

            for (int i = 0; i < images.Count; i++)
            {
                var r = new Random();
                int random = r.Next(images.Count);

                if (random != i)
                {
                    var tmp = images[i];
                    images[i] = images[random];
                    images[random] = tmp;
                }
            }

            for (int e = 0; e < numberOfEpochs; e++)
            {
                ArrayList alreadyWas = new ArrayList();

                for (int i = 0; i < images.Count; i++)
                {
                    //var r = new Random();
                    //int random = r.Next(images.Count);

                    //while (alreadyWas.Contains(random))
                    //    random = r.Next(images.Count);

                    //alreadyWas.Add(random);

                    Tuple<string, int> paths = images[i];
                    var imgName = paths.Item1;
                    var d = paths.Item2;

                    image = new Bitmap(imgName);

                    var x = GetPixels(image);

                    neuralNet.Learn(x.ToArray<float>(), d);

                    Eav[e] += neuralNet.E;
                }

                Eav[e] = Eav[e] / images.Count;
            }

            SaveNeuralNet();

            neuralNet.RecognizeTrainingSet();

            mainForm.ShowMessage("Обучение завершено!");
        }

        private float[] GetPixels(Bitmap image)
        {
            var x = new float[image.Width * image.Height];
            int k = 0;

            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    x[k++] = image.GetPixel(i, j).ToArgb() == Color.White.ToArgb() ? 0 : 1;
                }
            }

            return x;
        }

        public void SetPath(string path)
        {
            PathToLearningSet = path;
            mainForm.StartLearningButton = true;
            mainForm.NumberOfEpochsEnabled = true;
            mainForm.LearningSpeedEnabled = true;
        }

        private void SaveNeuralNet()
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = File.Create(FileName);
            formatter.Serialize(stream, neuralNet);
            stream.Close();
        }

        public void TryToRecognize(Bitmap image)
        {
            var x = GetPixels(image);

            int result = neuralNet.TryToRecognize(x);

            mainForm.ShowMessage(result.ToString());            
            mainForm.Result = result.ToString();
        }
    }
}
